package com.yulinlin.lang.util;


import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

public class ReflectionUtil {

	private static final String set_prefix = "set";

	private static final String get_prefix = "get";

	private static final String is_prefix = "is";

	public static final String sp_prefix = "\\.";



	public static boolean isNotEmpty(Object val){
		return  !isEmpty(val);
	}

	public static boolean isEmpty(Object val){
		if(val instanceof Collection){
			Collection coll = (Collection)val;
			return  coll.isEmpty();
		}
		if(val instanceof Number){
			return  false;
		} else if(val instanceof String){
			return  "".equals(val);
		}else{
			return  val == null;
		}
	}

	private static boolean methodWhere(Method method, Class... clazz) {
		int i = 0;
		for (Class<?> aClass : method.getParameterTypes()) {
			if (!aClass.isAssignableFrom(clazz[i++])) {
				return false;
			}
		}
		return true;
	}


	public static Method findMethod(Class clazz, String name, Class... paramsClass) {

		for (Method method : clazz.getMethods()) {
			if (method.getName().equals(name)) {
				if (methodWhere(method, paramsClass)) {
					return method;
				}
			}
		}
		return null;
	}


	public static Object invokeMethod(Object obj, String name, Object... params) {

		Class[] clazzs = new Class[params.length];
		int i = 0;
		for (Object param : params) {
			clazzs[i++] = param.getClass();
		}
		Method method = findMethod(obj.getClass(), name, clazzs);
		method.setAccessible(true);
		try {
			return method.invoke(obj, params);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}


	public static Field findField(Class clazz, String name) {

		return ReflectionUtils.findField(clazz, name);
	}

	private static Map<Class, List<Field>> fieldMap = new HashMap<>();

	/**
	 * 得到所有字段
	 *
	 * @param clazz
	 * @return
	 */
	public static List<Field> getAllDeclaredFields(Class<?> clazz) {
		List<Field> list = fieldMap.get(clazz);


		if (list == null) {
			list = new LinkedList<>();
			while (clazz != null) {
				for (Field field : clazz.getDeclaredFields()) {
					if (Modifier.isStatic(field.getModifiers())) {
						continue;
					}
					list.add(field);
				}
				clazz = clazz.getSuperclass();
			}
			fieldMap.put(clazz, list);
		}


		return list;
	}


	private static Method getGetMethod(Class clazz, String name) {
		String methodName = get_prefix + StringUtil.toUpperCaseFirstOne(name);

		Method method = ReflectionUtils.findMethod(clazz, methodName);

		return method;
	}

	private static Method getSetMethod(Class clazz, String name) {
		String methodName = set_prefix + StringUtil.toUpperCaseFirstOne(name);
		for (Method method : clazz.getMethods()) {
			if (method.getName().equals(methodName)) {
				return method;
			}
		}
		return null;
	}


	public static <E> E newInstance(Class<E> cla) {
		//	return ReflectUtil.newInstanceIfPossible(cla);

		try {
			return cla.newInstance();
		} catch (Exception e) {

			throw new RuntimeException(e);
		}

	}

	public static <E> E invokeGetter(Object obj, String[] names) {
		return invokeGetter(obj, Arrays.asList(names));
	}

	/**
	 * 调用Getter方法.
	 * 支持多级，如：对象名.对象名.方法
	 */
	public static <E> E invokeGetter(Object obj, List<String> names) {
		Object object = obj;
		for (String name : names) {
			if (object == null) {
				continue;
			}
			if (object.getClass().isArray()) {
				Object[] args = (Object[]) object;
				object = args[Integer.parseInt(name)];
			} else if (object instanceof List) {
				List list = (List) object;
				object = list.get(Integer.parseInt(name));
			} else if (object instanceof Map) {
				Map map = (Map) object;
				object = map.get(name);
			} else {
				Method method = getGetMethod(object.getClass(), name);
				if (method != null) {
					method.setAccessible(true);
					object = ReflectionUtils.invokeMethod(method, object);
				}else{
					Field field =  findField(obj.getClass(),name);
					field.setAccessible(true);
					try {
						object = field.get(obj);
					}catch (Exception e){
						throw new RuntimeException(e);
					}
				}

			}

		}
		return (E) object;
	}

	/**
	 * 调用Getter方法.
	 * 支持多级，如：对象名.对象名.方法
	 */
	public static <E> E invokeGetter(Object obj, String propertyName, E defVal) {
		String[] names = propertyName.split(sp_prefix);
		E val = (E) invokeGetter(obj, names);
		if (val != null) {
			return val;
		}
		return defVal;
	}

	/**
	 * 调用Getter方法.
	 * 支持多级，如：对象名.对象名.方法
	 */
	public static <E> E invokeGetter(Object obj, String propertyName) {
		String[] names = propertyName.split(sp_prefix);
		return (E) invokeGetter(obj, names);
	}

	/**
	 * 调用Setter方法, 仅匹配方法名。
	 * 支持多级，如：对象名.对象名.方法
	 */
	public static void invokeSetter(Object obj, String propertyName, Object value) {
		Object object = obj;
		String[] names = propertyName.split(sp_prefix);
		String key = names[names.length - 1];
		if (names.length >= 2) {
			String[] keys = new String[names.length - 1];
			for (int i = 0; i < names.length - 1; i++) {
				keys[i] = names[i];
			}
			object = invokeGetter(obj, keys);
		}
		if (object.getClass().isArray()) {
			Object[] args = (Object[]) object;
			args[Integer.parseInt(key)] = value;
		} else if (object instanceof List) {
			List list = (List) object;
			list.add(Integer.parseInt(key), value);
		} else if (object instanceof Map) {
			Map map = (Map) object;
			map.put(key, value);
		} else {
			Method method = getSetMethod(object.getClass(), key);
			if(method != null){
				method.setAccessible(true);
				ReflectionUtils.invokeMethod(method, object, value);
			}else{
				Field field =  findField(obj.getClass(),propertyName);
				field.setAccessible(true);
				try {
					field.set(obj,value);
				}catch (Exception e){
					throw new RuntimeException(e);
				}
			}



		}

	}

	public static <E> E copyProperties(E source, E target) {

		for (Field field : ReflectionUtil.getAllDeclaredFields(source.getClass())) {
			Object val =  ReflectionUtil.invokeGetter(source,field.getName());
			if(val == null){
				continue;
			}
			ReflectionUtil.invokeSetter(target,field.getName(),val);
		}

		return target;
	}




	public static boolean isGet(Method method){
		String name = method.getName();
		if(name.startsWith("get")){
			return true;
		} else if(name.startsWith("is")){
			return true;
		}
		return false;
	}


}